package cn.ddiancan.auth.config;

import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.Clock;
import java.time.Duration;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;

import cn.ddiancan.auth.handler.XddAuthenticationFailureHandler;
import cn.ddiancan.auth.handler.XddAuthenticationSuccessHandler;
import cn.ddiancan.auth.service.converter.XddOAuth2PasswordAuthenticationConverter;
import cn.ddiancan.auth.service.converter.XddSmsCodeAuthenticationConver;
import cn.ddiancan.auth.service.converter.XddUserCodeAuthenticationConverter;
import cn.ddiancan.auth.service.entrypoint.XddStatusAuthenticationEntryPoint;
import cn.ddiancan.auth.service.generator.XddJwtGenerator;
import cn.ddiancan.auth.service.generator.XddOAuth2RefreshTokenGenerator;
import cn.ddiancan.auth.service.provider.XddOAuth2PasswordAuthenticationProvider;
import cn.ddiancan.auth.service.provider.XddOAuth2RefreshTokenAuthenticationProvider;
import cn.ddiancan.auth.service.provider.XddOAuth2SmsCodeAuthenticationProvider;
import cn.ddiancan.auth.service.provider.XddOAuth2UserCodeAuthenticationProvider;
import cn.ddiancan.auth.service.userdetails.XddcloudUserDetailsImpl;
import cn.ddiancan.xddcloud.common.exception.ServiceException;
import cn.ddiancan.xddcloud.ddccore.cache.XddCloudCacheClient;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.DelegatingOAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2AccessTokenGenerator;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.DelegatingAuthenticationConverter;

@Configuration
public class Oauth2ServerConfiguation {

    @Value("${xddcloud.jwt.publicKey:}")
    private String publicKey;

    @Value("${xddcloud.jwt.privateKey:}")
    private String privateKey;

    @Value("${xddcloud.jwt.keyId:XDDCLOUD_JWT_KEYID}")
    private String jwtKeyId;

    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
        OAuth2TokenGenerator<?> tokenGenerator, JwtEncoder jwtEncoder, XddcloudUserDetailsImpl xddcloudUserDetails,
        PasswordEncoder passwordEncoder, OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer,
        AuthorizationServerSettings authorizationServerSettings, OAuth2AuthorizationService oAuth2AuthorizationService,
        XddCloudCacheClient xddCloudCacheClient) throws Exception {

        // 创建 OAuth2 授权服务器配置器
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
            OAuth2AuthorizationServerConfigurer.authorizationServer();

        // 配置 token 端点
        configureTokenEndpoint(authorizationServerConfigurer);

        // 配置 HTTP 安全设置
        http.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
            .with(authorizationServerConfigurer, Customizer.withDefaults()) // 将配置应用到 http 请求
            .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()); // 任何请求都需要认证
        createAuthenticationProviders(tokenGenerator, jwtEncoder, xddcloudUserDetails, passwordEncoder, jwtCustomizer,
            authorizationServerSettings, oAuth2AuthorizationService, xddCloudCacheClient).forEach(
            http::authenticationProvider);
        http.exceptionHandling(
            exceptions -> exceptions.authenticationEntryPoint(new XddStatusAuthenticationEntryPoint()));

        return http.build();
    }

    private void configureTokenEndpoint(OAuth2AuthorizationServerConfigurer authorizationServerConfigurer) {
        authorizationServerConfigurer.tokenEndpoint(
            oAuth2TokenEndpointConfigurer -> oAuth2TokenEndpointConfigurer.accessTokenRequestConverter(
                    new DelegatingAuthenticationConverter(Arrays.asList(new XddOAuth2PasswordAuthenticationConverter(),
                        new XddUserCodeAuthenticationConverter(), new XddSmsCodeAuthenticationConver())))
                .accessTokenResponseHandler(new XddAuthenticationSuccessHandler())
                .errorResponseHandler(new XddAuthenticationFailureHandler()));
    }

    private List<AuthenticationProvider> createAuthenticationProviders(OAuth2TokenGenerator<?> tokenGenerator,
        JwtEncoder jwtEncoder, XddcloudUserDetailsImpl xddcloudUserDetails, PasswordEncoder passwordEncoder,
        OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer,
        AuthorizationServerSettings authorizationServerSettings, OAuth2AuthorizationService oAuth2AuthorizationService,
        XddCloudCacheClient xddCloudCacheClient) {

        // 创建认证提供者数组
        return List.of(new AuthenticationProvider[]{
            new XddOAuth2PasswordAuthenticationProvider(tokenGenerator, jwtEncoder, xddcloudUserDetails,
                passwordEncoder, jwtCustomizer, authorizationServerSettings, oAuth2AuthorizationService),
            new XddOAuth2RefreshTokenAuthenticationProvider(oAuth2AuthorizationService, tokenGenerator),
            new XddOAuth2UserCodeAuthenticationProvider(tokenGenerator, jwtEncoder, xddcloudUserDetails, jwtCustomizer,
                authorizationServerSettings, oAuth2AuthorizationService),
            new XddOAuth2SmsCodeAuthenticationProvider(tokenGenerator, jwtEncoder, xddcloudUserDetails, jwtCustomizer,
                authorizationServerSettings, oAuth2AuthorizationService, xddCloudCacheClient)});
    }

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return web -> web.ignoring()
            .requestMatchers("/favicon.ico", "/css/**", "/error", "/actuator/**", "/login", "/logout", "/mobile/**");
    }

    @Bean
    public JwtEncoder jwtEncoder(JWKSource<SecurityContext> jwkSource) {
        return new NimbusJwtEncoder(jwkSource);
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
        JwtTimestampValidator jwtTimestampValidator = new JwtTimestampValidator();
        jwtTimestampValidator.setClock(Clock.offset(Clock.systemUTC(), Duration.ofHours(8)));
        DelegatingOAuth2TokenValidator<Jwt> jwtDelegatingOAuth2TokenValidator =
            new DelegatingOAuth2TokenValidator<>(Arrays.asList(jwtTimestampValidator));
        jwtDecoder.setJwtValidator(jwtDelegatingOAuth2TokenValidator);
        return jwtDecoder;
    }

    @Bean
    public OAuth2TokenGenerator<?> tokenGenerator(OAuth2TokenCustomizer<JwtEncodingContext> customTokenEnhancer,
        JwtEncoder jwtEncoder, XddcloudUserDetailsImpl userDetailsService) {
        XddJwtGenerator jwtGenerator = new XddJwtGenerator(jwtEncoder, customTokenEnhancer, userDetailsService);
        OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
        XddOAuth2RefreshTokenGenerator refreshTokenGenerator = new XddOAuth2RefreshTokenGenerator();
        return new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        try {
            byte[] privateKeyDecode = Base64.getDecoder().decode(privateKey);
            byte[] publicKeyEncoded = Base64.getDecoder().decode(publicKey);
            X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(publicKeyEncoded);
            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyDecode);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
            RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
            RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(jwtKeyId).build();
            return new ImmutableJWKSet<>(new JWKSet(rsaKey));
        } catch (Exception e) {
            throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "生成JWT密钥失败");
        }
    }
}
